#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <list>
#include <cstdio>
#include <exception>
#include <pthread.h>
#include "locker.h"
#include "sql_connection_pool.h"

template< typename T >
class threadpool{
public:
    threadpool( int actor_model, connection_pool *connPool, int thread_number = 8, int max_requests = 10000 );
    ~threadpool();
    bool append( T* request , int state );
    bool append_p(T *request);

private:
    //工作线程的运行函数，不断从等待队列中取出任务并执行
    static void* worker( void* arg ); 
    /*
    pthread_create的函数原型中第三个参数的类型为函数指针，指向的线程处理函数参数类型为(void *)
    若线程函数为类成员函数，则this指针会作为默认的参数被传进函数中，
    从而和线程函数参数(void*)不能匹配，不能通过编译。
    静态成员函数就没有这个问题，里面没有this指针。
    */
    void run();

private:
    int m_thread_number;//线程池中的线程数目
    int m_max_requests;//请求队列中最大的请求数
    pthread_t* m_threads;//描述线程池中的数组，大小是m_thread_number
    std::list< T* >m_workqueue;//请求队列
    locker m_queuelocker;//保护请求队列的互斥锁
    sem m_queuestat; //是否有任务需要处理
    bool m_stop; //是否结束线程
    connection_pool *m_connPool;//数据库
    int m_actor_model;//模型切换


};

template< typename T >
threadpool< T >::threadpool( int actor_model, connection_pool *connPool, int thread_number, int max_requests ): 
m_actor_model( actor_model ), m_thread_number( thread_number ), 
m_max_requests( max_requests ), 
m_stop( false ), m_threads( NULL ),m_connPool( connPool )
{
    if( ( thread_number <= 0 ) && ( max_requests <= 0 ) ){
        throw std::exception();
    }

    m_threads = new pthread_t[ m_thread_number ];
    if( !m_threads ){
        throw std::exception();
    }
    //创建thread_number个线程，并设置为脱离线程
    for( int i = 0; i < thread_number; i++ ){
        //printf( "create the %dth thread\n", i );
        /*pthread_create() 函数成功创建的线程会自动执行指定的函数，不需要手动开启。*/
        if( pthread_create( m_threads + i, NULL, worker, this ) != 0 ){
            delete [] m_threads;
            throw std::exception();
        }
        /*
        线程的两种状态
        joinable：线程结束后（通过pthread_exit结束或者线程执行体任务执行完毕）不会释放线程所占用堆栈和线程描述符（总计8K多）等资源，除非在主线程调用了pthread_join函数之后才会释放。
        pthread_join函数一般应用在主线程需要等待子线程结束后才继续执行的场景。(pthread_join是一个阻塞函数，调用方会阻塞到pthread_join所指定的tid的线程结束后才被回收，但是在此之前，调用方是霸占系统资源的。 )
        
        unjoinable：该线程结束后会自动释放占用资源。实现方式是在创建时指定属性，或者在线程执行体的最开始处添加一行：pthread_detach(pthread_self())；
        不会阻塞，调用它后，线程运行结束后会自动释放资源,后者非常方便。
        */ 
        if( pthread_detach( m_threads[i] ) ){
            delete [] m_threads;
            throw std::exception();
        }
    }
}

template< typename T >
threadpool< T >::~threadpool(){
    delete [] m_threads;
    m_stop = true;
}

template< typename T >
bool threadpool< T >::append( T* request,  int state ){
    m_queuelocker.lock();
    if( m_workqueue.size() > m_max_requests ){
        m_queuelocker.unlock();
        return false;
    }
    request->m_state = state;
    m_workqueue.push_back( request );
    m_queuelocker.unlock();
    m_queuestat.post();//有任务需要处理，发送信号V
    return true;
}

template <typename T>
bool threadpool<T>::append_p(T *request)
{
    m_queuelocker.lock();
    if (m_workqueue.size() >= m_max_requests)
    {
        m_queuelocker.unlock();
        return false;
    }
    m_workqueue.push_back(request);
    m_queuelocker.unlock();
    m_queuestat.post();
    return true;
}

template< typename T >
void* threadpool< T >::worker( void* arg ){
    threadpool* pool = (threadpool*) arg;
    pool->run();
    return pool;
}

template< typename T >
void threadpool< T >::run(){
    while( !m_stop ){
        m_queuestat.wait();//P
        m_queuelocker.lock();
        if( m_workqueue.empty() ){
            m_queuelocker.unlock();
            continue;
        }
        T* request = m_workqueue.front();
        m_workqueue.pop_front();
        m_queuelocker.unlock();
        if( !request ){
            continue;
        }
        if ( 1 == m_actor_model )
        {
            if ( 0 == request->m_state )
            {
                if ( request->read_once() )
                {
                    request->improv = 1;
                    connectionRAII mysqlcon(&request->mysql, m_connPool);
                    request->process();
                }
                else
                {
                    request->improv = 1;
                    request->timer_flag = 1;
                }
            }
            else
            {
                if (request->write())
                {
                    request->improv = 1;
                }
                else
                {
                    request->improv = 1;
                    request->timer_flag = 1;
                }
            }
        }
        else
        {
            connectionRAII mysqlcon(&request->mysql, m_connPool);
            request->process();
        }
    }
    
}

#endif // !THREADPOOL_H
